package net.shopxx.boot;

import java.util.HashSet;
import java.util.Properties;
import java.util.Set;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.cache.ehcache.EhCacheFactoryBean;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.task.TaskExecutor;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.i18n.FixedLocaleResolver;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import net.sf.ehcache.event.CacheEventListener;

@Configuration
@ComponentScan(basePackages = {"net.shopxx"},excludeFilters={@Filter(type=FilterType.ANNOTATION,value=Controller.class),@Filter(type=FilterType.ANNOTATION,value=ControllerAdvice.class)})
public class JPAConfig {
    
    @Autowired
    private Environment env;
    
    @Bean(destroyMethod = "close")
    public DataSource dataSource() throws Exception {
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        dataSource.setDriverClass(env.getProperty("jdbc.driver"));
        dataSource.setJdbcUrl(env.getProperty("jdbc.url"));
        dataSource.setUser(env.getProperty("jdbc.username"));
        dataSource.setPassword(env.getProperty("jdbc.password"));
        dataSource.setInitialPoolSize(Integer.valueOf(env.getProperty("connection_pools.initial_pool_size")));
        dataSource.setMinPoolSize(Integer.valueOf(env.getProperty("connection_pools.min_pool_size")));
        dataSource.setMaxPoolSize(Integer.valueOf(env.getProperty("connection_pools.max_pool_size")));
        dataSource.setMaxIdleTime(Integer.valueOf(env.getProperty("connection_pools.max_idle_time")));
        dataSource.setAcquireIncrement(Integer.valueOf(env.getProperty("connection_pools.acquire_increment")));
        dataSource.setCheckoutTimeout(Integer.valueOf(env.getProperty("connection_pools.checkout_timeout")));
        return dataSource;
    }
    
    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(@Autowired DataSource dataSource) {
        final LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
        emf.setPackagesToScan("net.shopxx.entity");
        HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
        hibernateJpaVendorAdapter.setShowSql(false);
        hibernateJpaVendorAdapter.setGenerateDdl(true);
        emf.setJpaVendorAdapter(hibernateJpaVendorAdapter);
        emf.setJpaProperties(getJpaProperties());
        emf.setDataSource(dataSource);
        return emf;
    }
    
    private Properties getJpaProperties() {
        final Properties properties = new Properties();
        properties.setProperty("hibernate.cache.use_second_level_cache", env.getProperty("hibernate.cache.use_second_level_cache"));
        properties.setProperty("hibernate.cache.region.factory_class", env.getProperty("hibernate.cache.region.factory_class"));
        properties.setProperty("hibernate.cache.use_query_cache", env.getProperty("hibernate.cache.use_query_cache"));
        properties.setProperty("hibernate.jdbc.fetch_size", env.getProperty("hibernate.jdbc.fetch_size"));
        properties.setProperty("hibernate.jdbc.batch_size", env.getProperty("hibernate.jdbc.batch_size"));
        properties.setProperty("hibernate.connection.isolation","2");
        properties.setProperty("hibernate.connection.release_mode","on_close");
        properties.setProperty("javax.persistence.validation.mode","none");
        properties.setProperty("hibernate.search.analyzer","net.shopxx.AnsjAnalyzer");
        properties.setProperty("hibernate.search.default.directory_provider","filesystem");
        properties.setProperty("hibernate.search.default.indexBase",FileUtils.getTempDirectoryPath()+"/shopxx_index");
        return properties;
    }

    @Bean
    public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(emf);
        return transactionManager;
    }

    
    @Bean
    public EhCacheManagerFactoryBean ehCacheManagerFactoryBean(){
        EhCacheManagerFactoryBean cacheManagerFactoryBean = new EhCacheManagerFactoryBean ();
        cacheManagerFactoryBean.setConfigLocation(new ClassPathResource("/ehcache.xml"));
        cacheManagerFactoryBean.setShared(true);
        return cacheManagerFactoryBean;
    }
    
    @Bean
    public EhCacheCacheManager ehCacheCacheManager(EhCacheManagerFactoryBean ehCacheManagerFactoryBean){
        return new EhCacheCacheManager(ehCacheManagerFactoryBean.getObject());
    }
    
    @Bean
    public EhCacheFactoryBean articleHitsEhCacheFactoryBean(EhCacheManagerFactoryBean ehCacheManagerFactoryBean,@Autowired net.shopxx.listener.CacheEventListener cacheEventListener){
        EhCacheFactoryBean ehCacheFactoryBean = new EhCacheFactoryBean();
        ehCacheFactoryBean.setCacheManager(ehCacheManagerFactoryBean.getObject());
        ehCacheFactoryBean.setCacheName("articleHits");
        Set<CacheEventListener> cacheEventListeners = new HashSet<>();
        cacheEventListeners.add(cacheEventListener);
        ehCacheFactoryBean.setCacheEventListeners(cacheEventListeners);
        return ehCacheFactoryBean;
    }
    
    @Bean
    public EhCacheFactoryBean productHitsEhCacheFactoryBean(EhCacheManagerFactoryBean ehCacheManagerFactoryBean,@Autowired net.shopxx.listener.CacheEventListener cacheEventListener){
        EhCacheFactoryBean ehCacheFactoryBean = new EhCacheFactoryBean();
        ehCacheFactoryBean.setCacheManager(ehCacheManagerFactoryBean.getObject());
        ehCacheFactoryBean.setCacheName("productHits");
        Set<CacheEventListener> cacheEventListeners = new HashSet<>();
        cacheEventListeners.add(cacheEventListener);
        ehCacheFactoryBean.setCacheEventListeners(cacheEventListeners);
        return ehCacheFactoryBean;
    }
    
    @Bean
    public FreeMarkerConfigurer freeMarkerConfigurer() {
        FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
        configurer.setTemplateLoaderPaths("classpath:/",env.getProperty("template.loader_path"));
        Properties freemarkerSettings = new Properties();
        freemarkerSettings.setProperty("default_encoding",env.getProperty("template.encoding"));
        freemarkerSettings.setProperty("url_escaping_charset",env.getProperty("url_escaping_charset"));
        freemarkerSettings.setProperty("output_format","HTMLOutputFormat");
        freemarkerSettings.setProperty("template_update_delay",env.getProperty("template.update_delay"));
        freemarkerSettings.setProperty("tag_syntax","auto_detect");
        freemarkerSettings.setProperty("classic_compatible","true");
        freemarkerSettings.setProperty("number_format",env.getProperty("template.number_format"));
        freemarkerSettings.setProperty("boolean_format",env.getProperty("template.boolean_format"));
        freemarkerSettings.setProperty("datetime_format",env.getProperty("template.datetime_format"));
        freemarkerSettings.setProperty("date_format",env.getProperty("template.date_format"));
        freemarkerSettings.setProperty("time_format",env.getProperty("template.time_format"));
        freemarkerSettings.setProperty("object_wrapper","freemarker.ext.beans.BeansWrapper");
        configurer.setFreemarkerSettings(freemarkerSettings);
        return configurer;
    }
    
    @Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasenames("WEB-INF/language/common/message","WEB-INF/language/shop/message","WEB-INF/language/member/message","WEB-INF/language/admin/message");
        messageSource.setUseCodeAsDefaultMessage(true);
        messageSource.setCacheSeconds(3600);
        messageSource.setDefaultEncoding("UTF-8");
        return messageSource;
    }
    
    @Bean
    public FixedLocaleResolver localeResolver(){
        return new FixedLocaleResolver();
    }
    
    @Bean
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setCorePoolSize(Integer.valueOf(env.getProperty("task.pool_size")));
        threadPoolTaskExecutor.setQueueCapacity(Integer.valueOf(env.getProperty("task.queue_capacity")));
        threadPoolTaskExecutor.initialize();
        return threadPoolTaskExecutor;
    }
}
